package vn.com.vega.music.downloadmanager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import vn.com.vega.chacha.R;
import vn.com.vega.music.clientserver.JsonBase;
import vn.com.vega.music.clientserver.JsonDownloadUrl;
import vn.com.vega.music.clientserver.JsonWatchUrl;
import vn.com.vega.music.database.DataStore;
import vn.com.vega.music.device.FileStorage;
import vn.com.vega.music.network.ContentDownloader;
import vn.com.vega.music.network.NetworkUtility;
import vn.com.vega.music.objects.Song;
import vn.com.vega.music.objects.Video;
import vn.com.vega.music.objects.WrapObjectDownload;
import vn.com.vega.music.utils.Const;
import vn.com.vega.music.utils.Util;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.RemoteViews;

public class DownloadService extends Service {
	private static String LOG_TAG = DownloadService.class.getSimpleName();

	// private List<Song> listSongDownloaded;
	private List<WrapObjectDownload> listSongVideoNeedDownload;
	private final ServiceStub mBinder = new ServiceStub(this);
	private DataStore dataStore = DataStore.getInstance();
	private WrapObjectDownload currentDownload;
	private int percentOfSongDownload;
	private int lastPercentOfSongDownload;
	private NotificationManager notificationManager;
	private boolean mServiceInuse = false;
	private int mStartId = -1;
	private boolean isDownloading = false;

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		mServiceInuse = true;
		mDelayHandler.removeCallbacksAndMessages(null);
		return mBinder;
	}

	@Override
	public void onRebind(Intent intent) {
		// TODO Auto-generated method stub
		mServiceInuse = true;
		mDelayHandler.removeCallbacksAndMessages(null);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		mStartId = startId;
		mServiceInuse = true;
		mDelayHandler.removeCallbacksAndMessages(null);
		Message msg = mDelayHandler.obtainMessage();
		mDelayHandler.sendMessageDelayed(msg, IDLE_TIME);

		return START_STICKY;
	}

	private static int IDLE_TIME = 60000;
	private Handler mDelayHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			// TODO: check if download in progess, so do not stop download
			removeCallbacksAndMessages(null);
			if (isDownloading) {
				sendMessageDelayed(msg, IDLE_TIME);
				return;
			}

			stopSelf(mStartId);
		}
	};

	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		mServiceInuse = false;
		if (isDownloading) {
			return true;
		}
		stopSelf(mStartId);
		return true;
	}

	public boolean addtoDownloadList(WrapObjectDownload objectDownload)
			throws RemoteException {
		// TODO Auto-generated method stub
		if (objectDownload.getRealObject() == null) {
			// Do not download unknow object
			return false;
		}
		if (listSongVideoNeedDownload.contains(objectDownload)) {
			return false;
		}

		listSongVideoNeedDownload.add(objectDownload);
		showNotifySongInProgress(objectDownload, 0);

		if (!isDownloading) {
			startDownload();
		}
		return true;
	}

	private void startDownload() {
		if (listSongVideoNeedDownload == null
				|| listSongVideoNeedDownload.size() == 0) {
			Message msg = mDelayHandler.obtainMessage();
			mDelayHandler.sendMessageDelayed(msg, IDLE_TIME);
			return;
		}
		isDownloading = true;
		currentDownload = listSongVideoNeedDownload.remove(0);
		DownloadSong downloadSong = new DownloadSong(currentDownload, this);
		downloadSong.execute();
	}

	static class DeletetFile extends AsyncTask<Void, Void, Void> {
		String filePath;

		public DeletetFile(String path) {
			filePath = path;
		}

		@Override
		protected Void doInBackground(Void... params) {
			// TODO Auto-generated method stub
			try {
				File file = new File(filePath);
				file.delete();
			} catch (Exception e) {
				// TODO: handle exception
			}
			return null;
		}

	}

	static class DownloadSong extends AsyncTask<Void, Integer, Void> {
		@Override
		protected void onProgressUpdate(Integer... values) {
			// TODO Auto-generated method stub
			super.onProgressUpdate(values);
			DownloadService downloadService = downloadServiceRfc.get();
			downloadService.showNotifySongInProgress(objectDownload,
					values[0].intValue());
		}

		// private DataStore dataStore;
		private WeakReference<DownloadService> downloadServiceRfc;
		private WrapObjectDownload objectDownload;
		private String errorMessage = null;
		private String pathFileOutput;

		public DownloadSong(WrapObjectDownload object,
				DownloadService downloadService) {
			downloadServiceRfc = new WeakReference<DownloadService>(
					downloadService);
			downloadService.dataStore = DataStore.getInstance();
			objectDownload = object;
		}

		@Override
		protected void onPostExecute(Void result) {
			// TODO Auto-generated method stub
			DownloadService downloadService = downloadServiceRfc.get();
			downloadService.isDownloading = false;
			if (isCancelled()) {
				DeletetFile del = new DeletetFile(pathFileOutput);
				del.execute();
				return;
			}
			if (!Util.checkStringIsemptyOrNull(errorMessage)) {
				downloadService.showNotify(errorMessage, objectDownload);
				DeletetFile del = new DeletetFile(pathFileOutput);
				del.execute();
				return;
			}

			// TODO: hide progress notify
			String message = "Tải thành công bài hát: "
					+ objectDownload.getTitle();
			downloadService.showNotify(message, objectDownload);
			if (downloadService.listSongVideoNeedDownload.size() > 0) {
				downloadService.startDownload();
			} else {
				Message msg = downloadService.mDelayHandler.obtainMessage();
				downloadService.mDelayHandler
						.sendMessageDelayed(msg, IDLE_TIME);
			}
		}

		@Override
		protected void onPreExecute() {
			// TODO Auto-generated method stub
			DownloadService downloadService = downloadServiceRfc.get();
			downloadService.isDownloading = true;
			// String message = "Đang tải bài hát: " + songToDownload.title;
			// showNotify(message, songToDownload.id);
			downloadService.showNotifySongInProgress(objectDownload, 0);
		}

		@Override
		protected Void doInBackground(Void... params) {
			// TODO Auto-generated method stub
			// check sd card status. If sdcard is full then break
			// dataStore.addSongDownloaded(songCurrentDownload);
			DownloadService downloadService = downloadServiceRfc.get();
			if (!FileStorage.isExtMounted()) {
				Log.e(LOG_TAG, "Error downloading: sdcard unmounted, song "
						+ objectDownload.getId());
				errorMessage = "Chưa có thẻ nhớ!!!";
				return null;
			}

			// Before download check if song is Downloaded
			if (objectDownload.isSong()) {
				if (downloadService.dataStore
						.checkSongIsDownloaded((Song) objectDownload
								.getRealObject())) {
					errorMessage = objectDownload.getObjectName() + " "
							+ objectDownload.getTitle() + " đã được tải về";
					return null;
				}
			}
			if (objectDownload.isVideo()) {
				if (downloadService.dataStore
						.checkVideoIsDownloaded((Video) objectDownload
								.getRealObject())) {
					errorMessage = objectDownload.getObjectName() + " "
							+ objectDownload.getTitle() + " đã được tải về";
					return null;
				}
			}

			// Get Url download object
			String downloadUrl = null;
			if (objectDownload.isSong()) {
				String audioProfileId = downloadService.dataStore
						.getConfig(Const.MUSIC_QUALITY_ID);
				JsonDownloadUrl jsonDownloadUrl = JsonDownloadUrl
						.loadDownloadUrl(objectDownload.getId(), audioProfileId);
				if (!jsonDownloadUrl.isSuccess()) {
					if (jsonDownloadUrl.getErrorCode() == JsonBase.ERROR_POWER_EXPIRE) {
						downloadService.isDownloading = false;
						downloadService.listSongVideoNeedDownload.clear();
						errorMessage = "Tài khoản của bạn đã hết quyền tải nhạc";
						return null;
					} else {
						Log.e(LOG_TAG, "Query download url error "
								+ jsonDownloadUrl.getErrorMessage());
						// Remove it from download pool
						errorMessage = "Gặp lỗi khi tải bài hát "
								+ objectDownload.getTitle();
						return null;
					}
				}
				downloadUrl = jsonDownloadUrl.downloadUrl;
			} else if (objectDownload.isVideo()) {
				JsonWatchUrl jsonWatchUrl = JsonWatchUrl
						.loadWatchUrl(objectDownload.getId());
				if (!jsonWatchUrl.isSuccess()) {
					if (jsonWatchUrl.getErrorCode() == JsonBase.ERROR_POWER_EXPIRE) {
						downloadService.isDownloading = false;
						downloadService.listSongVideoNeedDownload.clear();
						errorMessage = "Tài khoản của bạn đã hết quyền video";
						return null;
					} else {
						Log.e(LOG_TAG, "Query download url error "
								+ jsonWatchUrl.getErrorMessage());
						// Remove it from download pool
						errorMessage = "Gặp lỗi khi tải video "
								+ objectDownload.getTitle();
						return null;
					}

				}
				downloadUrl = jsonWatchUrl.streamingUrl;
			}
			if (Util.checkStringIsemptyOrNull(downloadUrl)) {
				errorMessage = "Gặp lỗi khi tải "
						+ objectDownload.getObjectName() + " "
						+ objectDownload.getTitle();
			}

			ContentDownloader cd = NetworkUtility.doDownloadContent(
					downloadUrl, null, null);
			InputStream networkInputStream = null;
			int totalSize = 0;

			// Get network stream
			try {
				networkInputStream = cd.getInputStream();
				totalSize = cd.getContentSize();
			} catch (IOException e) {
				Log.e(LOG_TAG, "Error downloading " + objectDownload.getId()
						+ ", error = " + e.getClass().getName() + ", msg = "
						+ e.getMessage());
				if (objectDownload.isSong()) {
					errorMessage = "Gặp lỗi khi tải bài hát "
							+ objectDownload.getTitle();
				} else if (objectDownload.isVideo()) {
					errorMessage = "Gặp lỗi khi tải video "
							+ objectDownload.getTitle();
				}
				return null;
			}

			// Check quota first
			if (!Util.checkQuota(totalSize)) {
				try {
					networkInputStream.close();
				} catch (IOException ioe) {
				}

				Log.d(LOG_TAG, "DownloadManager stopped due to disk quota!");
				errorMessage = "Bộ nhớ không đủ để tải về !";
				return null;
			}

			// Step temp: call FileStorage to get saved path for this
			// song then create output stream
			FileOutputStream localFileOutputStream = null;
			File localFile = null;
			try {
				// Open file to write
				FileStorage _fileStorage = new FileStorage();
				if (objectDownload.isSong()) {
					pathFileOutput = _fileStorage.createSongCachePath(
							objectDownload.getTitle(), "mp3");
				} else if (objectDownload.isVideo()) {
					pathFileOutput = _fileStorage.createVideoCachePath(
							objectDownload.getTitle(), "mp4");
				}

				localFile = new File(pathFileOutput);
				localFileOutputStream = new FileOutputStream(localFile);
			} catch (Exception e) {
				Log.e(LOG_TAG, "Error creating file to download "
						+ objectDownload.getId() + ", error = "
						+ e.getClass().getName() + ", msg = " + e.getMessage());
				if (networkInputStream != null) {
					try {
						networkInputStream.close();
					} catch (IOException ioe) {
					}
				}

				if (localFileOutputStream != null) {
					try {
						localFileOutputStream.close();
					} catch (IOException ioe) {
					}
				}
				errorMessage = "Ghi file ra thẻ nhớ thất bại!";
				return null;
			}

			// Step 4: loop to download and send onSongDownloadProgress
			int downloadedSize = 0;
			byte[] buffer = new byte[5120]; // create a buffer...
			int bufferLength = 0;
			int subCounter = 0;
			while (downloadedSize < totalSize) {
				// Read
				try {
					bufferLength = networkInputStream.read(buffer);
				} catch (IOException e) {
					Log.e(LOG_TAG,
							"Error downloading: read failed, song "
									+ objectDownload.getId() + ", error = "
									+ e.getClass().getName() + ", msg = "
									+ e.getMessage());
					if (objectDownload.isSong()) {
						errorMessage = "Tải thất bại bài hát "
								+ objectDownload.getTitle();
					} else if (objectDownload.isVideo()) {
						errorMessage = "Tải thất bại video "
								+ objectDownload.getTitle();
					}

					return null;
				}
				if (bufferLength < 0) {
					break;
				}

				// Save
				try {
					localFileOutputStream.write(buffer, 0, bufferLength);
				} catch (IOException e) {
					Log.e(LOG_TAG,
							"Error downloading: write failed, song "
									+ objectDownload.getId() + ", error = "
									+ e.getClass().getName() + ", msg = "
									+ e.getMessage());
					errorMessage = "Tải thất bại "
							+ objectDownload.getObjectName() + ": "
							+ objectDownload.getTitle();
					return null;
				}
				downloadedSize += bufferLength;

				subCounter += bufferLength;
				downloadService.lastPercentOfSongDownload = downloadService.percentOfSongDownload;
				downloadService.percentOfSongDownload = downloadedSize * 100
						/ totalSize;
				// if (percentOfSongDownload - lastPercentOfSongDownload > 5) {
				publishProgress(downloadService.percentOfSongDownload);
				// }
			}

			// Close file and stream first, seperate into two try & catch
			// because we have to close two streams
			try {
				localFileOutputStream.close();
			} catch (IOException ioe) {
			}
			try {
				networkInputStream.close();
			} catch (IOException ioe) {
			}

			if (downloadedSize >= totalSize) {
				if (objectDownload.isSong()) {
					Song song = (Song) objectDownload.getRealObject();
					song.cached_file_path = pathFileOutput;
					downloadService.dataStore
							.addSongDownloaded((Song) objectDownload
									.getRealObject());
				} else if (objectDownload.isVideo()) {
					Video video = (Video) objectDownload.getRealObject();
					video.cached_file_path = pathFileOutput;
					downloadService.dataStore
							.addVideoDownloade((Video) objectDownload
									.getRealObject());
				}

			} else {
				errorMessage = "Tải thất bại " + objectDownload.getObjectName()
						+ ": " + objectDownload.getTitle();
				return null;
			}
			return null;
		}

	}

	public int getPercentDownloadOfObject(WrapObjectDownload object) {
		// TODO Auto-generated method stub
		if (currentDownload == object) {
			return percentOfSongDownload;
		} else {
			return 0;
		}
	}

	// public void downloadListSong(List<WrapObjectDownload> listObjectDownload)
	// throws RemoteException {
	// // TODO Auto-generated method stub
	// for (WrapObjectDownload download : listObjectDownload) {
	// addtoDownloadList(download);
	// }
	// }

	public WrapObjectDownload getCurrentObjectDownload() {
		// TODO Auto-generated method stub
		return currentDownload;
	}

	public void stopDownload() {
		// TODO Auto-generated method stub
		for (Object downloadObject : listSongVideoNeedDownload) {
			if (downloadObject instanceof Song) {
				notificationManager.cancel(((Song) downloadObject).id);
			} else if (downloadObject instanceof Video) {
				notificationManager.cancel(Integer.valueOf(
						((Video) downloadObject).id).intValue());
			}

		}
		listSongVideoNeedDownload.clear();
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		dataStore = DataStore.getInstance();
		listSongVideoNeedDownload = new ArrayList<WrapObjectDownload>();
		// listSongDownloaded = dataStore.getListDownloadedSongs();
		// if (listSongDownloaded == null) {
		// listSongDownloaded = new ArrayList<Song>();
		// }
		notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
	}

	private void showNotify(String message, WrapObjectDownload objectDownload) {
		if (Util.checkStringIsemptyOrNull(message)) {
			return;
		}
		if (objectDownload.getRealObject() == null) {
			return;
		}

		int icon = R.drawable.ic_app;
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
				new Intent(), 0);
		Notification noti = new NotificationCompat.Builder(this)
				.setContentTitle(this.getString(R.string.app_name))
				.setContentText(message).setSmallIcon(icon)
				.setContentIntent(pendingIntent).setAutoCancel(true).build();

		notificationManager.cancel(objectDownload.getId());
		notificationManager.notify(objectDownload.getId(), noti);

	}

	private void showNotifySongInProgress(WrapObjectDownload downloadObject,
			int progress) {

		NotificationCompat.Builder builder2 = new NotificationCompat.Builder(
				this);
		builder2.setSmallIcon(R.drawable.ic_notyfi_now_downloading);
		builder2.setTicker("ChaCha - " + downloadObject.getTitle());
		builder2.setWhen(System.currentTimeMillis());
		Notification notification = builder2.build();

		notification.flags = notification.flags
				| Notification.FLAG_ONGOING_EVENT;
		PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
				new Intent(), 0);
		notification.contentIntent = pendingIntent;
		notification.contentView = new RemoteViews(getApplicationContext()
				.getPackageName(), R.layout.notify_show_progress);
		notification.contentView.setImageViewResource(R.id.status_icon,
				R.drawable.ic_notyfi_now_downloading);
		notification.contentView.setTextViewText(R.id.status_text,
				"Đang tải " + downloadObject.getObjectName() + " "
						+ downloadObject.getTitle() + " - " + progress + "%");
		notification.contentView.setProgressBar(R.id.status_progress, 100,
				progress, false);
		notificationManager.notify(downloadObject.getId(), notification);
	}

	private void cancelAllNotify() {
		for (WrapObjectDownload object : listSongVideoNeedDownload) {
			notificationManager.cancel(object.getId());
		}
	}

	/**
	 * 
	 * @author Vega Corp
	 * 
	 */
	static class ServiceStub extends IDownloadService.Stub {
		WeakReference<DownloadService> mService;

		public ServiceStub(DownloadService service) {
			mService = new WeakReference<DownloadService>(service);
		}

		@Override
		public boolean addSongtoDownloadList(Song songNeddDownload)
				throws RemoteException {
			// TODO Auto-generated method stub
			if (mService.get() != null) {
				return mService.get().addtoDownloadList(
						new WrapObjectDownload(songNeddDownload));
			}
			return false;
		}

		@Override
		public boolean songExitInDownloadList(Song song) throws RemoteException {
			// TODO Auto-generated method stub
			if (song == null) {
				return false;
			}
			if (mService.get() != null) {
				return mService.get().songExitInDownloadList(song);
			}
			return false;
		}

		@Override
		public int getPercentDownloadOfSong(Song song) throws RemoteException {
			// TODO Auto-generated method stub
			if (mService.get() != null) {
				return mService.get().getPercentDownloadOfObject(
						new WrapObjectDownload(song));
			}
			return -1;
		}

		// @Override
		// public Song getCurrentSongDownload() throws RemoteException {
		// // TODO Auto-generated method stub
		// if (mService.get() != null) {
		// mService.get().getCurrentSongDownload();
		// }
		// return null;
		// }

		@Override
		public void stopDownload() throws RemoteException {
			// TODO Auto-generated method stub
			if (mService.get() != null) {
				mService.get().stopDownload();
			}
		}

		@Override
		public boolean addListSongtoDownloadList(List<Song> songNeddDownload)
				throws RemoteException {
			// TODO Auto-generated method stub
			if (mService.get() != null) {
				return mService.get().addListSongtoDownloadList(
						songNeddDownload);
			}
			return false;
		}

		@Override
		public boolean addVideoToDownloadList(Video videoNeedDownload)
				throws RemoteException {
			// TODO Auto-generated method stub
			if (videoNeedDownload == null) {
				return false;
			}
			if (mService != null) {
				return mService.get().addtoDownloadList(
						new WrapObjectDownload(videoNeedDownload));
			}
			return false;
		}

		@Override
		public boolean videoExitInDownloadList(Video video)
				throws RemoteException {
			// TODO Auto-generated method stub
			if (video == null) {
				return false;
			}
			WrapObjectDownload wrapObjectDownload =  new WrapObjectDownload(video);
			if (mService == null) {
				mService.get().addtoDownloadList(wrapObjectDownload);
				return true;
			}
			return false;
		}

		@Override
		public int getPercentDownloadOfVideo(Video video)
				throws RemoteException {
			// TODO Auto-generated method stub
			return 0;
		}
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		cancelAllNotify();
	}

	private boolean songExitInDownloadList(Song song) {
		if (song == null) {
			return false;
		}
		WrapObjectDownload wrapObjectDownload = new WrapObjectDownload(song);
		return listSongVideoNeedDownload.contains(wrapObjectDownload);
	}

	private boolean videoExitInDownloadList(Video video) {
		if (video == null) {
			return false;
		}
		WrapObjectDownload wrapObjectDownload = new WrapObjectDownload(video);
		return listSongVideoNeedDownload.contains(wrapObjectDownload);
	}

	public boolean addListSongtoDownloadList(List<Song> songNeddDownload) {
		// TODO Auto-generated method stub
		for (int i = 0; i < songNeddDownload.size(); i++) {
			Song song = songNeddDownload.get(i);
			if (songExitInDownloadList(song)) {
				songNeddDownload.remove(i);
				i--;
			}
		}
		if (songNeddDownload.size() > 0) {
			for (Song song : songNeddDownload) {
				WrapObjectDownload wrapObjectDownload = new WrapObjectDownload(
						song);
				listSongVideoNeedDownload.add(wrapObjectDownload);
				showNotifySongInProgress(wrapObjectDownload, 0);
			}
			if (!isDownloading) {
				startDownload();
			}
		}

		return true;
	}
}
